BigQuery へのアクセスを IAM Conditions とタグで制御する方法のまとめ

BigQuery へのアクセスを IAM Conditions とタグで制御する方法のまとめ

Clock Icon2024.10.12

Google Cloud データエンジニアのはんざわです。
過去のブログで BigQuery のデータセットへのアクセスをタグで制御する方法を紹介しました。

https://dev.classmethod.jp/articles/bigquery-access-control-tag/

本ブログでは、過去のブログで紹介しきれなかった内容をまとめて紹介したいと思います。
具体的には、タグでアクセスを制御する関数の紹介やアクセス制御の挙動の確認、ユースケースについて触れていきます。

また、前回のブログを投稿した時は、タグを使用した BigQuery のテーブルへのアクセス制限の機能はプレビューでしたが、2024年6月27日に正式に GA になっています。
本機能も併せて紹介したいと思います。

June 27, 2024

You can now use tags on BigQuery tables to conditionally grant or deny access with Identity and Access Management (IAM) policies. This feature is generally available (GA). You can also attach tags to BigQuery datasets during dataset creation to conditionally grant or deny access with IAM policies.

IAM Conditions とタグでアクセスを制御する方法

IAM Conditions とタグでアクセスを制御する場合、大きく分けて2つの関数で条件を設定することができます。

  1. 指定したキーのタグが付いているかどうか
  2. 指定したキーとバリューのタグが付いているかどうか

IAM Conditionsでは、設定した条件が true と評価されると、アクセスが許可されます。

また、それぞれの関数は以下のとおりで、IAM Conditions の条件エディタで使用することができます。

# 1
resource.hasTagKey(
  keyName: string
) trending_flat -> bool

# 2
resource.matchTag(
  keyName: string,
  valueShortName: string
) trending_flat -> bool

例として、env というキーが存在し、そのキーに devprod という値が設定されていると仮定します。

バリューに関係なく、 env のキーが付いている全てのリソースを対象としたい場合は、前者の resource.hasTagKey が使用できます。
一方で、キーが env で、且つバリューが dev のリソースを対象としたい場合は、後者の resource.matchTag が使用できます。

また、タグのキーとバリューには、作成時に一意の ID が割り当てられます。
略称だけでなく、この ID で条件を設定することも可能で、関数は以下になります。

# 1
resource.hasTagKeyId(
  keyName: string
) trending_flat -> bool

# 2
resource.matchTagId(
  keyName: string,
  valueShortName: string
) trending_flat -> bool

参考元:リソースタグ

検証内容

今回の検証内容は、クエリを発行するサービスアカウントに IAM Conditions で条件を付与し、そのサービスアカウントが各操作を行った際の挙動を確認してみたいと思います。

条件

  1. 特定のキーのダグが付与されているリソースのみ許可
  2. 特定のキーのダグが付与されていないリソースのみ許可
  3. 特定のキーとバリューのタグが付与されているリソースのみ許可
  4. 特定のキーとバリューのタグが付与されていないリソースのみ許可

操作

  1. bq ls でデータセットが表示されるか
  2. bq ls 許可されているデータセット でテーブルが表示されるか
  3. bq ls 許可されていないデータセット でテーブルが表示されるか
  4. bq query 許可されているデータセットの通常のテーブル にクエリを発行できるか
  5. bq query 許可されているデータセットの許可されているテーブル にクエリを発行できるか
  6. bq query 許可されているデータセットの許可されていないテーブル にクエリを発行できるか
  7. bq query 許可されていないデータセットの通常のテーブル にクエリを発行できるか
  8. bq query 許可されていないデータセットの許可されているテーブル にクエリを発行できるか
  9. bq query 許可されていないデータセットの許可されていないテーブル にクエリを発行できるか

まとめると次の表のようになります。

  • bq ls
bq ls bq ls 許可されているデータセット bq ls 許可されていないデータセット
特定のキーのダグが付与されているリソースのみ許可
特定のキーのダグが付与されていないリソースのみ許可
特定のキーとバリューのタグが付与されているリソースのみ許可
特定のキーとバリューのタグが付与されていないリソースのみ許可
  • bq query
許可されているデータセットの通常のテーブル 許可されているデータセットの許可されているテーブル 許可されているデータセットの許可されていないテーブル 許可されていないデータセットの通常のテーブル 許可されていないデータセットの許可されているテーブル 許可されていないデータセットの許可されていないテーブル
特定のキーのダグが付与されているリソースのみ許可
特定のキーのダグが付与されていないリソースのみ許可
特定のキーとバリューのタグが付与されているリソースのみ許可
特定のキーとバリューのタグが付与されていないリソースのみ許可

事前準備

早速、検証に必要なリソースを作成します。

1. タグの作成

1つのキーに1つのバリューが紐づいている2つのタグをプロジェクトレベルで作成します。
今回の例だと、teamenv がキーで dadev がバリューに相当します。

  • team

    • da
  • env

    • dev

2. サービスアカウントの作成

各条件ごとに4つのサービスアカウントを作成します。
それぞれのアカウントに BigQuery ジョブユーザーBigQuery データ閲覧者 の権限をプロジェクトレベルで割り当ててます。

  • sa-team-allowed@<PROJECT_ID>.iam.gserviceaccount.com
    • 特定のキーのダグが付与されているリソースにのみ許可
  • sa-team-denied@<PROJECT_ID>.iam.gserviceaccount.com
    • 特定のキーのダグが付与されていないリソースにのみ許可
  • sa-team-da-allowed@<PROJECT_ID>.iam.gserviceaccount.com
    • 特定のキーとバリューのタグが付与されているリソースにのみ許可
  • sa-team-da-denied@<PROJECT_ID>.iam.gserviceaccount.com
    • 特定のキーとバリューのタグが付与されていないリソースのみ許可

3. IAM Conditions で条件を付与

4つのサービスアカウントの BigQuery データ閲覧者 の権限に IAM Conditions で条件を付与していきます。
IAM Conditions の条件エディタで、以下の条件式を追記します。

sa-team-allowed
resource.hasTagKey("<PROJECT_ID>/team")
sa-team-denied
!resource.hasTagKey("<PROJECT_ID>/team")
sa-team-da-allowed
resource.matchTag("<PROJECT_ID>/team", "da")
sa-team-da-denied
!resource.matchTag("<PROJECT_ID>/team", "da")

参考:条件の CEL

4. データセットとテーブルの作成

3つのデータセットとその配下に適当なテーブルを作成します。
2つのデータセットには、daml のタグを付与します。
残り1つのデータセットには、タグを付与せず、その配下のテーブルにタグを付与します。

  • データセット:team_da_dataset - team/da
    • テーブル:sample_table
    • テーブル:sample_table_da - team/da
    • テーブル:sample_table_dev - env/dev
  • データセット:team_dataset
    • テーブル:sample_table
    • テーブル:sample_table_da - team/da
    • テーブル:sample_table_dev - env/dev

検証

ここからは、用意したリソースを使って検証を進めたいと思います。

検証の結果

先に検証の結果から紹介すると、以下の図のようになりました。

  • bq ls
bq ls bq ls 許可されているデータセット bq ls 許可されていないデータセット
特定のキーのダグが付与されているリソースのみ許可 タグが付与されているデータセット タグが付与されていないテーブルを含む全てのテーブル ×
特定のキーのダグが付与されていないリソースのみ許可 タグが付与されていないデータセット タグが付与されていないテーブルを含む全てのテーブル ×
特定のキーとバリューのタグが付与されているリソースのみ許可 タグが付与されているデータセット タグが付与されていないテーブルを含む全てのテーブル ×
特定のキーとバリューのタグが付与されていないリソースのみ許可 タグが付与されていないデータセット タグが付与されていないテーブルを含む全てのテーブル ×
  • bq query
許可されているデータセットの通常のテーブル 許可されているデータセットの許可されているテーブル 許可されているデータセットの許可されていないテーブル 許可されていないデータセットの通常のテーブル 許可されていないデータセットの許可されているテーブル 許可されていないデータセットの許可されていないテーブル
特定のキーのダグが付与されているリソースのみ許可 × ×
特定のキーのダグが付与されていないリソースのみ許可 × × × ×
特定のキーとバリューのタグが付与されているリソースのみ許可 × ×
特定のキーとバリューのタグが付与されていないリソースのみ許可 × × × ×

考察

  • resource.hasTagKeyresource.matchTag の関数の評価における挙動に違いは無いと思われる(適用範囲がキーだけか、キーとバリューかの違いだけ)
  • 肯定の関数では、データセットへのアクセスが可能であれば、その配下のテーブルへのアクセスは可能であった
  • さらに、データセットへのアクセスが不可能でも、その配下のテーブルへのアクセスが可能であればアクセスすることができた
  • 否定の関数では、条件となった特定のタグが付いているリソースにアクセスすることは、完全に不可能であった
  • 肯定の関数では可能であったが、否定の関数では、データセットへのアクセスが不可能だと、その配下のテーブルへのアクセスも不可能である

検証の過程

以下のブログで紹介したように、サービスアカウントの権限を借用して、各コマンドを実行してみます。

https://dev.classmethod.jp/articles/gcp-gcloud-command-service-account/

1. 特定のキーのダグが付与されているリソースのみ許可

# bq ls でデータセットが表示されるか
$ bq ls

>    datasetId     
 ----------------- 
  team_da_dataset  

# bq ls 許可されているデータセット でテーブルが表示されるか
$ bq ls team_da_dataset

>     tableId        Type    Labels   Time Partitioning   Clustered Fields  
 ------------------ ------- -------- ------------------- ------------------ 
  sample_table       TABLE                                                  
  sample_table_da    TABLE                                                  
  sample_table_dev   TABLE  

# bq ls 許可されていないデータセット でテーブルが表示されるか	
$ bq ls team_dataset

> BigQuery error in ls operation: Access Denied: Dataset <PROJECT_ID>:team_dataset: Permission bigquery.tables.list denied on dataset <PROJECT_ID>:team_dataset (or it may not exist).
# bq query 許可されているデータセットの通常のテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されているテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_da'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されていないテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_dev'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されていないデータセットの通常のテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r31592a81bc9bbbf4_000001927a7be32d_1': Access Denied: Table <PROJECT_ID>:team_dataset.sample_table: User does
not have permission to query table <PROJECT_ID>:team_dataset.sample_table, or perhaps it does not exist.

# bq query 許可されていないデータセットの許可されているテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_da'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されていないデータセットの許可されていないテーブル
bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_dev'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r2abc821e0c5a8ad6_000001927a806441_1': Access Denied: Table <PROJECT_ID>:team_dataset.sample_table_dev: User
does not have permission to query table <PROJECT_ID>:team_dataset.sample_table_dev, or perhaps it does not exist.

2. 特定のキーのダグが付与されていないリソースのみ許可

# bq ls でデータセットが表示されるか
$ bq ls

>  datasetId    
 -------------- 
  team_dataset 

# bq ls 許可されているデータセット でテーブルが表示されるか
$ bq ls team_dataset

>     tableId        Type    Labels   Time Partitioning   Clustered Fields  
 ------------------ ------- -------- ------------------- ------------------ 
  sample_table       TABLE                                                  
  sample_table_da    TABLE                                                  
  sample_table_dev   TABLE  

# bq ls 許可されていないデータセット でテーブルが表示されるか	
$ bq ls team_da_dataset

> BigQuery error in ls operation: Access Denied: Dataset <PROJECT_ID>:team_da_dataset: Permission bigquery.tables.list denied on dataset <PROJECT_ID>:team_da_dataset (or it may not exist).
# bq query 許可されているデータセットの通常のテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されているテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_dev'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されていないテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_da'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r664a24b76e13da83_000001927a93601f_1': Access Denied: Table <PROJECT_ID>:team_dataset.sample_table_da: User does
not have permission to query table <PROJECT_ID>:team_dataset.sample_table_da, or perhaps it does not exist.

# bq query 許可されていないデータセットの通常のテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r6aa58e490ab49628_000001927a9decde_1': Access Denied: Table <PROJECT_ID>:team_da_dataset.sample_table: User does
not have permission to query table <PROJECT_ID>:team_da_dataset.sample_table, or perhaps it does not exist.

# bq query 許可されていないデータセットの許可されているテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_dev'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r42255aa28a146d11_000001927a9f52ae_1': Access Denied: Table <PROJECT_ID>:team_da_dataset.sample_table_dev: User
does not have permission to query table <PROJECT_ID>:team_da_dataset.sample_table_dev, or perhaps it does not exist.

# bq query 許可されていないデータセットの許可されていないテーブル
bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_da'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r7b3a7b2c6bbcef44_000001927aa05bb7_1': Access Denied: Table <PROJECT_ID>:team_da_dataset.sample_table_da: User
does not have permission to query table <PROJECT_ID>:team_da_dataset.sample_table_da, or perhaps it does not exist.

3. 特定のキーとバリューのタグが付与されているリソースのみ許可

# bq ls でデータセットが表示されるか
$ bq ls

>    datasetId     
 ----------------- 
  team_da_dataset  

# bq ls 許可されているデータセット でテーブルが表示されるか
$ bq ls team_da_dataset

>     tableId        Type    Labels   Time Partitioning   Clustered Fields  
 ------------------ ------- -------- ------------------- ------------------ 
  sample_table       TABLE                                                  
  sample_table_da    TABLE                                                  
  sample_table_dev   TABLE  

# bq ls 許可されていないデータセット でテーブルが表示されるか	
$ bq ls team_dataset

> BigQuery error in ls operation: Access Denied: Dataset <PROJECT_ID>:team_dataset: Permission bigquery.tables.list denied on dataset <PROJECT_ID>:team_dataset (or it may not exist).
# bq query 許可されているデータセットの通常のテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されているテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_da'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されていないテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_dev'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されていないデータセットの通常のテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r24504a733a4c615e_000001927aad371f_1': Access Denied: Table <PROJECT_ID>:team_dataset.sample_table: User does
not have permission to query table <PROJECT_ID>:team_dataset.sample_table, or perhaps it does not exist.

# bq query 許可されていないデータセットの許可されているテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_da'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されていないデータセットの許可されていないテーブル
bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_dev'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r56a6ec73791ca899_000001927ab4da88_1': Access Denied: Table <PROJECT_ID>:team_dataset.sample_table_dev: User
does not have permission to query table <PROJECT_ID>:team_dataset.sample_table_dev, or perhaps it does not exist.

4. 特定のキーとバリューのタグが付与されていないリソースのみ許可

# bq ls でデータセットが表示されるか
$ bq ls

>  datasetId    
 -------------- 
  team_dataset 

# bq ls 許可されているデータセット でテーブルが表示されるか
$ bq ls team_dataset

>     tableId        Type    Labels   Time Partitioning   Clustered Fields  
 ------------------ ------- -------- ------------------- ------------------ 
  sample_table       TABLE                                                  
  sample_table_da    TABLE                                                  
  sample_table_dev   TABLE  

# bq ls 許可されていないデータセット でテーブルが表示されるか	
$ bq ls team_da_dataset

> BigQuery error in ls operation: Access Denied: Dataset <PROJECT_ID>:team_da_dataset: Permission bigquery.tables.list denied on dataset <PROJECT_ID>:team_da_dataset (or it may not exist).
# bq query 許可されているデータセットの通常のテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されているテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_dev'

>
+-----+
| f0_ |
+-----+
|   1 |
+-----+

# bq query 許可されているデータセットの許可されていないテーブル にクエリを発行できるか
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_dataset.sample_table_da'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r58e434e7d893788d_000001927abbf9bb_1': Access Denied: Table <PROJECT_ID>:team_dataset.sample_table_da: User does
not have permission to query table <PROJECT_ID>:team_dataset.sample_table_da, or perhaps it does not exist.

# bq query 許可されていないデータセットの通常のテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r45cf68805f791068_000001927abd5d73_1': Access Denied: Table <PROJECT_ID>:team_da_dataset.sample_table: User does
not have permission to query table <PROJECT_ID>:team_da_dataset.sample_table, or perhaps it does not exist.

# bq query 許可されていないデータセットの許可されているテーブル
$ bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_dev'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r1220037fa25add24_000001927abde560_1': Access Denied: Table <PROJECT_ID>:team_da_dataset.sample_table_dev: User
does not have permission to query table <PROJECT_ID>:team_da_dataset.sample_table_dev, or perhaps it does not exist.

# bq query 許可されていないデータセットの許可されていないテーブル
bq query --nouse_legacy_sql 'SELECT COUNT(*) FROM team_da_dataset.sample_table_da'

> BigQuery error in query operation: Error processing job '<PROJECT_ID>:bqjob_r77f0d197698c834c_000001927abe76db_1': Access Denied: Table <PROJECT_ID>:team_da_dataset.sample_table_da: User
does not have permission to query table <PROJECT_ID>:team_da_dataset.sample_table_da, or perhaps it does not exist.

ユースケース

  • 大前提として、データの利用者側でデータの閲覧に制限がかかる恐れがあるユーザーには、プロジェクトレベルで BigQuery データ閲覧者 の権限を割り当てないようにしましょう。

  • データセットレベルで権限を割り当てたり、利用者の集合毎にプロジェクトを切り離したりすることで必要最低限のデータのみを閲覧できるようにしましょう。

  • その上で、次のような条件に当てはまる場合は、タグによるアクセス制御の選択肢も考えられると思います。

    • どうしてもプロジェクトレベルで BigQuery データ閲覧者 の権限を割り当てる必要があるが、特定のデータは閲覧できないようにしたい
    • 既にプロジェクトレベルで BigQuery データ閲覧者 の権限を割り当てているが、特定のデータを閲覧できないようにしたい。また、扱っているデータが多いため、権限の振り直しが非常に手間である
  • しかし、タグとタグに関する権限を管理する必要が出てくるので、利用には気をつけましょう。

まとめ

今回のブログでは、BigQuery へのアクセスを IAM Conditions とタグで制御する方法をまとめて紹介しました。
非常に長い内容となりましたが、参考になれば幸いです。

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.